Mustahkam, kengaytiriladigan va turi xavfsiz real-time ilovalar uchun TypeScript WebSocket'ni o'zlashtiring. Global auditoriya uchun eng yaxshi amaliyotlar, keng tarqalgan xatolar va ilg'or usullarni o'rganing.
TypeScript WebSocket: Turi Xavfsizligi bilan Real-time Muloqotni Yuksaltirish
Bugungi o'zaro bog'langan raqamli dunyoda real-time muloqot endi tor doiradagi xususiyat emas; bu zamonaviy veb-ilovalarning tamal toshidir. Tezkor xabarlashuv va hamkorlikdagi tahrirlashdan tortib, jonli sport yangiliklari va moliyaviy savdo platformalarigacha, foydalanuvchilar bir zumda javob va uzluksiz o'zaro ta'sirni kutishadi. WebSocket'lar bunga erishish uchun de-fakto standartga aylandi va kliyentlar hamda serverlar o'rtasida doimiy, to'liq dupleks aloqa kanallarini taklif etadi. Biroq, JavaScript'ning dinamik tabiati va WebSocket xabar tuzilmalarining murakkabligi ko'pincha ish vaqtidagi xatolarga, qiyin disk raskadrovkaga va dasturchi unumdorligining pasayishiga olib kelishi mumkin. Aynan shu yerda TypeScript o'zining kuchli tur tizimini WebSocket'lar olamiga olib kiradi va real-time dasturlashni potensial xatoliklar maydonidan oldindan aytib bo'ladigan va mustahkam tajribaga aylantiradi.
WebSocket'lar yordamida Real-time Muloqotning Kuchi
TypeScript'ning rolini chuqur o'rganishdan oldin, keling, nima uchun WebSocket'lar real-time ilovalar uchun bunchalik muhim ekanligini qisqacha ko'rib chiqaylik.
- Doimiy Ulanish: An'anaviy HTTP so'rov-javob sikllaridan farqli o'laroq, WebSocket'lar uzoq muddatli, ikki tomonlama ulanish o'rnatadi. Bu ulanishlarni qayta-qayta ochish va yopish bilan bog'liq ortiqcha yukni yo'q qiladi, bu esa tez-tez ma'lumot almashish uchun uni juda samarali qiladi.
- To'liq Dupleks Muloqot: Ham kliyent, ham server bir-biridan mustaqil ravishda va bir vaqtning o'zida ma'lumot yuborishi mumkin, bu esa haqiqatan ham interaktiv tajribalarni ta'minlaydi.
- Past Kechikish: Doimiy tabiat va kamaytirilgan ortiqcha yuk sezilarli darajada past kechikishga yordam beradi, bu hatto millisekundlar ham muhim bo'lgan ilovalar uchun juda muhimdir.
- Kengaytiriluvchanlik: Yaxshi arxitektura qilingan WebSocket serverlari ko'p sonli bir vaqtda ulanishlarni boshqara oladi va millionlab foydalanuvchilarga ega ilovalarni qo'llab-quvvatlaydi.
Quyidagi kabi ilovalar haqida o'ylab ko'ring:
- Global Chat Ilovalari: WhatsApp, Telegram va Slack kabi platformalar xabarlarni qit'alar bo'ylab bir zumda yetkazib berish uchun WebSocket'larga tayanadi.
- Hamkorlikdagi Vositalar: Google Docs, Figma va Miro real-time rejimida o'zgarishlarni sinxronlashtirish uchun WebSocket'lardan foydalanadi, bu esa bir nechta foydalanuvchiga bir vaqtning o'zida bir xil hujjat yoki maket ustida ishlash imkonini beradi.
- Moliyaviy Savdo Platformalari: Real-time fond birjasi kurslari, buyurtma yangilanishlari va narx ogohlantirishlari butun dunyodagi treyderlar uchun juda muhim bo'lib, ular WebSocket kanallari orqali ta'minlanadi.
- Onlayn O'yinlar: Ko'p o'yinchili o'yinlar o'yinchilarning harakatlari va o'yin holatini bir zumda sinxronlashtirishni talab qiladi, bu WebSocket'lar uchun mukammal foydalanish holatidir.
JavaScript WebSocket'larining Qiyinchiliklari
WebSocket'lar katta kuch taklif qilsa-da, ularni oddiy JavaScript'da amalga oshirish bir nechta qiyinchiliklarni keltirib chiqaradi, ayniqsa ilovalar murakkablashgan sari:
- Dinamik Ma'lumotlar Tuzilmalari: WebSocket xabarlari ko'pincha JSON obyektlari bo'ladi. Qattiq sxemasiz, bu obyektlar turli xil tuzilmalarga, etishmayotgan xususiyatlarga yoki noto'g'ri ma'lumot turlariga ega bo'lishi mumkin. Bu mavjud bo'lmagan yoki kutilmagan turdagi xususiyatlarga kirishga harakat qilganda ish vaqtidagi xatolarga olib kelishi mumkin.
- Xatolarga Moyil Xabarlarni Boshqarish: Dasturchilar kiruvchi xabarlarni sinchkovlik bilan tahlil qilishlari, ularning tuzilishini tekshirishlari va yuzaga kelishi mumkin bo'lgan tahlil xatolarini boshqarishlari kerak. Bu qo'lda tekshirish zerikarli va nazoratdan chetda qolishga moyil.
- Tur Nomuvofiqligi: Kliyent va server o'rtasida ma'lumotlarni uzatish, agar ehtiyotkorlik bilan boshqarilmasa, tur nomuvofiqligiga olib kelishi mumkin. Masalan, kliyentdan yuborilgan raqam serverda satr sifatida qabul qilinishi va kutilmagan xatti-harakatlarga olib kelishi mumkin.
- Disk raskadrovka Qiyinchiliklari: Real-time, asinxron muhitda xabar formatlari va tur nomuvofiqligi bilan bog'liq muammolarni disk raskadrovka qilish juda qiyin bo'lishi mumkin. Ma'lumotlar oqimini kuzatish va xatoning asosiy sababini aniqlash dasturchining ko'p vaqtini olishi mumkin.
- Refaktoring Xatarlari: Bo'sh belgilangan xabar tuzilmalariga tayanadigan kodni refaktoring qilish xavflidir. Xabar formatidagi kichik o'zgarish uni ushlaydigan statik tahlilsiz kutilmagan joylarda aloqani uzishi mumkin.
TypeScript bilan tanishuv: WebSocket Dasturlash uchun Paradigmalar O'zgarishi
TypeScript, statik turlashni qo'shadigan JavaScript'ning ustki to'plami, WebSocket dasturlashiga bo'lgan yondashuvimizni tubdan o'zgartiradi. Ma'lumotlar tuzilmalaringiz uchun aniq turlarni belgilash orqali siz ish vaqtida emas, balki kompilyatsiya vaqtida xatolarni ushlaydigan xavfsizlik tarmog'iga ega bo'lasiz.
TypeScript WebSocket Muloqotini Qanday Yaxshilaydi
TypeScript WebSocket dasturlashiga bir nechta asosiy afzalliklarni olib keladi:
- Kompilyatsiya Vaqtidagi Xatolarni Aniqlash: Eng muhim afzallik - bu kodingiz ishga tushishidan oldin tur bilan bog'liq xatolarni ushlash. Agar siz turli obyektda mavjud bo'lmagan xususiyatga kirishga yoki noto'g'ri turdagi ma'lumotlarni uzatishga harakat qilsangiz, TypeScript buni kompilyatsiya paytida belgilaydi va sizni potentsial ish vaqtidagi nosozliklardan qutqaradi.
- Kodning O'qilishi va Qo'llab-quvvatlanishini Yaxshilash: Aniq turlar kodingizni o'z-o'zini hujjatlashtiradigan qiladi. Dasturchilar yuborilayotgan va qabul qilinayotgan ma'lumotlarning kutilayotgan tuzilmasi va turlarini osongina tushunishlari mumkin, bu esa yangi jamoa a'zolarini jalb qilishni va vaqt o'tishi bilan kod bazasini saqlashni osonlashtiradi.
- Dasturchi Unumdorligini Oshirish: Kuchli turlash va aqlli kodni to'ldirish (IntelliSense) yordamida dasturchilar kodni tezroq va katta ishonch bilan yozishlari mumkin. IDE siz yozayotganingizda aniq takliflar berishi va potentsial muammolarni aniqlashi mumkin.
- Mustahkam Ma'lumotlarni Tekshirish: WebSocket xabarlaringiz uchun interfeyslar yoki turlarni belgilash orqali siz ma'lumotlar tuzilmasi uchun shartnomani tabiiy ravishda majburiy qilasiz. Bu ham kliyent, ham serverda keng ko'lamli qo'lda tekshirish mantiqiga bo'lgan ehtiyojni kamaytiradi.
- Refaktoringni Osonlashtiradi: Xabar tuzilmalaringizni refaktoring qilishingiz kerak bo'lganda, TypeScript'ning tur tekshiruvi darhol ilovangizning barcha ta'sirlangan qismlarini ko'rsatib beradi, bu esa o'zgarishlarning izchil va to'g'ri qo'llanilishini ta'minlaydi.
TypeScript bilan Amaliy Qo'llash
Keling, TypeScript yordamida turi xavfsiz WebSocket'larni qanday amalga oshirishni ko'rib chiqaylik.
1. Xabar Turlarini Aniqlash
Birinchi qadam - TypeScript interfeyslari yoki turlari yordamida WebSocket xabarlaringiz tuzilishini aniqlash. Bu ham chiquvchi, ham kiruvchi xabarlar uchun juda muhimdir.
Misol: Kliyentdan Serverga Xabarlar
Foydalanuvchilar xabar yuborishi va xonalarga qo'shilishi mumkin bo'lgan chat ilovasini tasavvur qiling. Kliyent tomonidan boshlangan harakatlar uchun turlarni qanday aniqlashingiz mumkinligi:
// types.ts
// Matnli xabar yuborish uchun interfeys
export interface SendMessagePayload {
roomId: string;
message: string;
}
// Xonaga qo'shilish uchun interfeys
export interface JoinRoomPayload {
roomId: string;
userId: string;
}
// Kliyentdan serverga yuborilishi mumkin bo'lgan barcha xabarlar uchun birlashma turi
export type ClientToServerEvent =
| { type: 'SEND_MESSAGE', payload: SendMessagePayload }
| { type: 'JOIN_ROOM', payload: JoinRoomPayload };
Diskriminantlangan birlashmadan foydalanish (har bir xabar turining o'ziga xos `type` xususiyatiga ega bo'lishi) TypeScript'dagi kuchli naqshdir. Bu serverda turli xabar turlarini aniq boshqarish imkonini beradi.
Misol: Serverdan Kliyentga Xabarlar
Xuddi shunday, serverdan kliyentga yuborilgan xabarlar uchun turlarni aniqlang:
// types.ts (davomi)
// Chat xonasida qabul qilingan xabar uchun interfeys
export interface ChatMessage {
id: string;
roomId: string;
senderId: string;
content: string;
timestamp: number;
}
// Foydalanuvchi xonaga qo'shilganligi haqidagi bildirishnoma uchun interfeys
export interface UserJoinedRoomPayload {
userId: string;
roomId: string;
timestamp: number;
}
// Serverdan kliyentga yuborilishi mumkin bo'lgan barcha xabarlar uchun birlashma turi
export type ServerToClientEvent =
| { type: 'NEW_MESSAGE', payload: ChatMessage }
| { type: 'USER_JOINED', payload: UserJoinedRoomPayload }
| { type: 'ERROR', payload: { message: string } };
2. Serverni Yaratish (Node.js va `ws` kutubxonasi bilan)
Keling, mashhur `ws` kutubxonasidan foydalangan holda oddiy Node.js serverini ko'rib chiqaylik. TypeScript integratsiyasi juda oddiy.
// server.ts
import WebSocket, { WebSocketServer } from 'ws';
import { ClientToServerEvent, ServerToClientEvent, ChatMessage, JoinRoomPayload, SendMessagePayload } from './types'; // types.ts fayli bir xil papkada deb taxmin qilinadi
const wss = new WebSocketServer({ port: 8080 });
console.log('WebSocket serveri 8080 portida ishga tushdi');
wss.on('connection', (ws: WebSocket) => {
console.log('Kliyent ulandi');
ws.on('message', (message: string) => {
try {
const parsedMessage: ClientToServerEvent = JSON.parse(message);
switch (parsedMessage.type) {
case 'SEND_MESSAGE':
handleSendMessage(ws, parsedMessage.payload);
break;
case 'JOIN_ROOM':
handleJoinRoom(ws, parsedMessage.payload);
break;
default:
console.warn('Noma\'lum xabar turi qabul qilindi:', parsedMessage);
sendError(ws, 'Noma\'lum xabar turi');
}
} catch (error) {
console.error('Xabarni tahlil qilishda xato:', error);
sendError(ws, 'Yaroqsiz JSON qabul qilindi');
}
});
ws.on('close', () => {
console.log('Kliyent uzildi');
});
ws.on('error', (error) => {
console.error('WebSocket xatosi:', error);
});
// Kliyentga xush kelibsiz xabarini yuborish
sendServerMessage(ws, { type: 'SYSTEM_INFO', payload: { message: 'Real-time serveriga xush kelibsiz!' } });
});
// Serverdan kliyentga xabar yuborish uchun yordamchi funksiya
function sendServerMessage(ws: WebSocket, message: ServerToClientEvent): void {
ws.send(JSON.stringify(message));
}
// Kliyentga xatolarni yuborish uchun yordamchi funksiya
function sendError(ws: WebSocket, errorMessage: string): void {
sendServerMessage(ws, { type: 'ERROR', payload: { message: errorMessage } });
}
// Maxsus xabar ishlovchilari
function handleSendMessage(ws: WebSocket, payload: SendMessagePayload): void {
console.log(`${payload.roomId} xonasida xabar qabul qilindi: ${payload.message}`);
// Haqiqiy ilovada siz buni xonadagi boshqa foydalanuvchilarga tarqatasiz
const newMessage: ChatMessage = {
id: Date.now().toString(), // Oddiy ID yaratish
roomId: payload.roomId,
senderId: 'anonymous', // Haqiqiy ilovada bu autentifikatsiyadan keladi
content: payload.message,
timestamp: Date.now()
};
// Misol: Barcha kliyentlarga tarqatish (xonaga xos tarqatish bilan almashtiring)
wss.clients.forEach(client => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
sendServerMessage(client, { type: 'NEW_MESSAGE', payload: newMessage });
}
});
// Ixtiyoriy ravishda yuboruvchiga tasdiqnoma yuborish
sendServerMessage(ws, { type: 'MESSAGE_SENT', payload: { messageId: newMessage.id } });
}
function handleJoinRoom(ws: WebSocket, payload: JoinRoomPayload): void {
console.log(`Foydalanuvchi ${payload.userId} ${payload.roomId} xonasiga qo'shilmoqda`);
// Haqiqiy ilovada siz xona obunalarini boshqarasiz va ehtimol boshqalarga tarqatasiz
const userJoinedNotification: UserJoinedRoomPayload = {
userId: payload.userId,
roomId: payload.roomId,
timestamp: Date.now()
};
// Xonadagi boshqalarga tarqatish (misol)
wss.clients.forEach(client => {
// Bu qaysi kliyent qaysi xonada ekanligini bilish uchun mantiqni talab qiladi
// Sodda bo'lishi uchun, bu yerda misol tariqasida hammaga yuboramiz
if (client.readyState === WebSocket.OPEN) {
sendServerMessage(client, { type: 'USER_JOINED', payload: userJoinedNotification });
}
});
}
// To'liqlik uchun faraziy SYSTEM_INFO xabar turi uchun ishlovchi qo'shish
// Bu serverning tuzilgan ma'lumotlarni qanday yuborishi mumkinligiga misol
// Eslatma: Yuqoridagi `sendServerMessage` chaqiruvida biz allaqachon 'SYSTEM_INFO' turini qo'shdik
// Biz buni aniqlik uchun bu yerda belgilaymiz, garchi u dastlabki `ServerToClientEvent` birlashmasining bir qismi bo'lmasa ham
// Haqiqiy ilovada barcha belgilangan turlar birlashmaning bir qismi ekanligiga ishonch hosil qilasiz
interface SystemInfoPayload {
message: string;
}
// Yuqoridagi kodni kompilyatsiya qilish uchun SYSTEM_INFO'ni ServerToClientEvent'ga qo'shishimiz kerak
// Bu misol uchun, u qo'shilgan deb faraz qilaylik:
// export type ServerToClientEvent = ... | { type: 'SYSTEM_INFO', payload: SystemInfoPayload };
// Bu izchil tur ta'riflariga bo'lgan ehtiyojni ko'rsatadi.
Eslatma: Yuqoridagi misol kodi `types.ts` mavjudligini va `ServerToClientEvent`ning to'liq kompilyatsiya uchun `SYSTEM_INFO` va `MESSAGE_SENT` turlarini o'z ichiga olgan holda yangilanganligini taxmin qiladi. Bu sizning xabar turlaringiz uchun yagona haqiqat manbasini saqlash muhimligini ta'kidlaydi.
3. Kliyentni Yaratish (Brauzer)
Kliyent tomonida siz mahalliy `WebSocket` API yoki `socket.io-client` kabi kutubxonadan foydalanasiz (garchi to'g'ridan-to'g'ri WebSocket uchun mahalliy API ko'pincha etarli bo'lsa ham). Tur xavfsizligi printsipi o'zgarishsiz qoladi.
// client.ts
import { ClientToServerEvent, ServerToClientEvent, ChatMessage, UserJoinedRoomPayload } from './types'; // types.ts fayli bir xil papkada deb taxmin qilinadi
const socket = new WebSocket('ws://localhost:8080');
// WebSocket ulanishi uchun voqea ishlovchilari
socket.onopen = () => {
console.log('WebSocket ulanishi o\'rnatildi');
// Misol: Ulanishdan so'ng xonaga qo'shilish
const joinRoomMessage: ClientToServerEvent = {
type: 'JOIN_ROOM',
payload: { roomId: 'general', userId: 'user123' }
};
sendMessage(joinRoomMessage);
};
socket.onmessage = (event) => {
try {
const message: ServerToClientEvent = JSON.parse(event.data as string);
switch (message.type) {
case 'NEW_MESSAGE':
handleNewMessage(message.payload);
break;
case 'USER_JOINED':
handleUserJoined(message.payload);
break;
case 'ERROR':
console.error('Server xatosi:', message.payload.message);
break;
case 'SYSTEM_INFO':
console.log('Tizim:', message.payload.message);
break;
case 'MESSAGE_SENT':
console.log('Xabar muvaffaqiyatli yuborildi, ID:', message.payload.messageId);
break;
default:
console.warn('Noma\'lum server xabari turi qabul qilindi:', message);
}
} catch (error) {
console.error('Server xabarini tahlil qilishda xato:', error);
}
};
socket.onclose = (event) => {
if (event.wasClean) {
console.log(`Ulanish toza yopildi, kod=${event.code} sabab=${event.reason}`);
} else {
console.error('Ulanish uzildi');
}
};
socket.onerror = (error) => {
console.error('WebSocket xatosi:', error);
};
// Kliyentdan serverga xabar yuborish funksiyasi
function sendMessage(message: ClientToServerEvent): void {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(message));
} else {
console.warn('WebSocket ochiq emas. Xabar yuborib bo\'lmadi.');
}
}
// Ulanishdan so'ng chat xabari yuborish misoli
function sendChatMessage(room: string, text: string) {
const message: ClientToServerEvent = {
type: 'SEND_MESSAGE',
payload: { roomId: room, message: text }
};
sendMessage(message);
}
// Kliyentdagi xabar ishlovchilari
function handleNewMessage(message: ChatMessage): void {
console.log(`
--- ${message.roomId} xonasida yangi xabar ---
Kimdan: ${message.senderId}
Vaqt: ${new Date(message.timestamp).toLocaleTimeString()}
Mazmuni: ${message.content}
---------------------------
`);
// UI'ni yangi xabar bilan yangilash
}
function handleUserJoined(payload: UserJoinedRoomPayload): void {
console.log(`Foydalanuvchi ${payload.userId} ${payload.roomId} xonasiga ${new Date(payload.timestamp).toLocaleTimeString()} da qo'shildi`);
// Yangi foydalanuvchini xonada ko'rsatish uchun UI'ni yangilash
}
// Foydalanish misoli:
// setTimeout(() => {
// sendChatMessage('general', 'Salom, dunyo!');
// }, 3000);
4. TypeScript bilan `ws` Kutubxonasidan Foydalanish
`ws` kutubxonasining o'zi ajoyib TypeScript yordamini taqdim etadi. Uni o'rnatganingizda (`npm install ws @types/ws`), siz WebSocket server nusxasi va alohida ulanishlar bilan ishlashda xavfsizroq kod yozishga yordam beradigan tur ta'riflarini olasiz.
5. Global Ilovalar Uchun E'tiborga Olinadigan Jihatlar
Global auditoriya uchun real-time ilovalarni yaratishda bir nechta omillar muhim ahamiyat kasb etadi va TypeScript ularning ba'zilarini boshqarishga yordam beradi:
- Vaqt Mintaqalari: Misollarimizda `timestamp` bilan ko'rsatilganidek, vaqt belgilarini har doim UTC yoki Epoch millisekundlarida yuboring. Keyin kliyent ularni foydalanuvchining mahalliy vaqt mintaqasiga muvofiq formatlashi mumkin. Tur xavfsizligi `timestamp`ning har doim raqam bo'lishini ta'minlaydi.
- Mahalliylashtirish: Xato xabarlari yoki tizim bildirishnomalari xalqarolashtirilishi kerak. TypeScript to'g'ridan-to'g'ri i18n bilan shug'ullanmasa-da, u uzatilayotgan mahalliylashtirilgan xabarlar tuzilmasining izchil bo'lishini ta'minlashi mumkin. Masalan, `ServerError` xabarida `code` va `params` maydonlari bo'lishi mumkin, bu esa kliyentdagi mahalliylashtirish mantiqining kerakli ma'lumotlarga ega bo'lishini ta'minlaydi.
- Ma'lumotlar Formatlari: Raqamli ma'lumotlar (masalan, narxlar, miqdorlar) qanday taqdim etilishida izchillikni ta'minlang. TypeScript bularning har doim raqam bo'lishini majburlab, tahlil qilish muammolarining oldini oladi.
- Autentifikatsiya va Avtorizatsiya: To'g'ridan-to'g'ri WebSocket xususiyati bo'lmasa-da, xavfsiz muloqot juda muhimdir. TypeScript autentifikatsiya tokenlari uchun kutilayotgan yuklamani va avtorizatsiya javoblari qanday tuzilganligini aniqlashga yordam beradi.
- Kengaytiriluvchanlik va Chidamlilik: TypeScript serveringizni sehrli ravishda kengaytiriladigan qila olmaydi, lekin xatolarni erta aniqlash orqali u kengaytirish osonroq bo'lgan barqarorroq ilovalarga hissa qo'shadi. Kliyentda mustahkam qayta ulanish strategiyalarini amalga oshirish ham muhimdir.
WebSocket'lar uchun Ilg'or TypeScript Patternlari
Asosiy tur ta'riflaridan tashqari, bir nechta ilg'or TypeScript naqshlari WebSocket dasturlashingizni yanada yaxshilashi mumkin:
1. Moslashuvchan Xabarlarni Boshqarish uchun Generiklar
Generiklar sizning xabar ishlovchi funksiyalaringizni ko'proq qayta ishlatiladigan qilishi mumkin.
// types.ts (kengaytirilgan)
// Har qanday serverdan-kliyentga voqea uchun umumiy interfeys
export interface ServerEvent {
type: string;
payload: T;
}
// Generiklarni yashirincha ishlatgan holda yangilangan ServerToClientEvent
export type ServerToClientEvent =
| ServerEvent & { type: 'NEW_MESSAGE' }
| ServerEvent & { type: 'USER_JOINED' }
| ServerEvent<{ message: string }> & { type: 'ERROR' }
| ServerEvent<{ message: string }> & { type: 'SYSTEM_INFO' }
| ServerEvent<{ messageId: string }> & { type: 'MESSAGE_SENT' };
// Kliyent tomonida generiklar yordamida qabul qiluvchi funksiya misoli
function handleServerMessage(event: MessageEvent, expectedType: string, handler: (payload: T) => void): void {
try {
const rawMessage = JSON.parse(event.data as string) as ServerEvent;
if (rawMessage.type === expectedType) {
handler(rawMessage.payload as T);
}
} catch (error) {
console.error(`${expectedType} turidagi xabarni qayta ishlashda xato:`, error);
}
}
// client.ts da foydalanish:
// socket.onmessage = (event) => {
// handleServerMessage(event, 'NEW_MESSAGE', handleNewMessage);
// handleServerMessage(event, 'USER_JOINED', handleUserJoined);
// handleServerMessage<{ message: string }>(event, 'ERROR', (payload) => {
// console.error('Server xatosi:', payload.message);
// });
// // ... va hokazo
// };
2. WebSocket Mantiqini Klasslar/Servislarga Abstraksiyalash
Kattaroq ilovalar uchun WebSocket mantiqini klasslar yoki servislar ichiga joylashtirish modullik va sinovdan o'tkazish imkoniyatini oshiradi. Siz ulanish, xabar yuborish va qabul qilishni boshqaradigan `WebSocketService` yaratishingiz mumkin, bu esa xom WebSocket API'sini abstraksiyalash imkonini beradi.
// WebSocketService.ts
import { EventEmitter } from 'events';
import { ClientToServerEvent, ServerToClientEvent } from './types';
interface WebSocketServiceOptions {
url: string;
reconnectInterval?: number;
maxReconnectAttempts?: number;
}
export class WebSocketService extends EventEmitter {
private socket: WebSocket | null = null;
private url: string;
private reconnectInterval: number;
private maxReconnectAttempts: number;
private reconnectAttempts: number = 0;
private isConnecting: boolean = false;
constructor(options: WebSocketServiceOptions) {
super();
this.url = options.url;
this.reconnectInterval = options.reconnectInterval || 5000;
this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
}
connect(): void {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
console.log('Allaqaqchon ulangan.');
return;
}
if (this.isConnecting) {
console.log('Ulanish jarayonda...');
return;
}
this.isConnecting = true;
console.log(`${this.url} ga ulanishga urinilmoqda...`);
this.socket = new WebSocket(this.url);
this.socket.onopen = this.onOpen;
this.socket.onmessage = this.onMessage;
this.socket.onclose = this.onClose;
this.socket.onerror = this.onError;
}
private onOpen = (): void => {
console.log('WebSocket ulanishi o\'rnatildi.');
this.reconnectAttempts = 0; // Muvaffaqiyatli ulanishda qayta ulanish urinishlarini tiklash
this.isConnecting = false;
this.emit('open');
};
private onMessage = (event: MessageEvent): void => {
try {
const message = JSON.parse(event.data as string) as ServerToClientEvent;
this.emit('message', message);
} catch (error) {
console.error('Xabarni tahlil qilishda xato:', error);
this.emit('error', new Error('Yaroqsiz JSON qabul qilindi'));
}
};
private onClose = (event: CloseEvent): void => {
console.log(`WebSocket ulanishi yopildi. Kod: ${event.code}, Sabab: ${event.reason}`);
this.isConnecting = false;
this.emit('close', event);
if (event.code !== 1000) { // 1000 - normal yopilish
this.reconnect();
}
};
private onError = (error: Event): void => {
console.error('WebSocket xatosi:', error);
this.isConnecting = false;
this.emit('error', error);
// Barcha xatolarda avtomatik qayta ulanmang, agar iloji bo'lsa, xato turiga bog'liq
};
private reconnect(): void {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Maksimal qayta ulanish urinishlariga erishildi. To\'xtatilmoqda.');
this.emit('maxReconnects');
return;
}
this.reconnectAttempts++;
console.log(`Qayta ulanishga urinilmoqda (${this.reconnectAttempts}/${this.maxReconnectAttempts}) ${this.reconnectInterval}ms dan so'ng...`);
setTimeout(() => {
this.connect();
}, this.reconnectInterval);
}
send(message: ClientToServerEvent): void {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
} else {
console.warn('WebSocket ochiq emas. Xabar yuborilmadi.');
// Ixtiyoriy ravishda xabarlarni navbatga qo'yish yoki xatolik chiqarish
}
}
close(): void {
if (this.socket) {
this.socket.close();
}
}
}
// Ilovangizning komponent/modulida foydalanish misoli:
// import { WebSocketService } from './WebSocketService';
//
// const wsService = new WebSocketService({ url: 'ws://localhost:8080', reconnectInterval: 3000 });
//
// wsService.on('open', () => {
// console.log('Ulandi!');
// wsService.send({ type: 'SEND_MESSAGE', payload: { roomId: 'general', message: 'Servisdan salom!' } });
// });
//
// wsService.on('message', (message: ServerToClientEvent) => {
// console.log('Servis orqali qabul qilindi:', message);
// if (message.type === 'NEW_MESSAGE') {
// // handleNewMessage(message.payload);
// }
// });
//
// wsService.on('error', (error) => {
// console.error('Servisda xatolik yuz berdi:', error);
// });
//
// wsService.on('close', () => {
// console.log('Servis uzildi.');
// });
//
// wsService.connect();
3. Ish vaqtidagi xavfsizlik uchun Tur Himoyachilari (Type Guards)
TypeScript kompilyatsiya vaqtida xavfsizlikni ta'minlasa-da, ba'zida siz tashqi manbalardan ma'lumotlar olishingiz yoki turlarni kafolatlay olmaydigan eski kodingiz bo'lishi mumkin. Tur himoyachilari yordam berishi mumkin:
// types.ts (kengaytirilgan)
// Umumiy xabar uchun interfeys
interface GenericMessage {
type: string;
payload: any;
}
// Xabarning ma'lum bir turga mansubligini tekshirish uchun tur himoyachisi
function isSendMessagePayload(payload: any): payload is SendMessagePayload {
return (
payload &&
typeof payload.roomId === 'string' &&
typeof payload.message === 'string'
);
}
// Server mantiqida tur himoyachisidan foydalanish
// ... wss.on('message') ishlovchisi ichida ...
// const parsedMessage: any = JSON.parse(message);
//
// if (parsedMessage && typeof parsedMessage.type === 'string') {
// switch (parsedMessage.type) {
// case 'SEND_MESSAGE':
// if (isSendMessagePayload(parsedMessage.payload)) {
// handleSendMessage(ws, parsedMessage.payload);
// } else {
// sendError(ws, 'SEND_MESSAGE uchun yaroqsiz yuklama');
// }
// break;
// // ... boshqa holatlar
// }
// } else {
// sendError(ws, 'Yaroqsiz xabar formati');
// }
TypeScript WebSocket Dasturlash uchun Eng Yaxshi Amaliyotlar
TypeScript'ning WebSocket'lar bilan afzalliklarini maksimal darajada oshirish uchun ushbu eng yaxshi amaliyotlarni ko'rib chiqing:
- Turlar uchun Yagona Haqiqat Manbai: Barcha xabar interfeyslari va turlaringiz uchun maxsus faylni (masalan, `types.ts`) saqlang. Ham kliyent, ham server bir xil ta'riflardan foydalanishiga ishonch hosil qiling.
- Diskriminantlangan Birlashmalar: Xabar turlari uchun diskriminantlangan birlashmalardan foydalaning. Bu bir nechta xabar turlarini boshqarishda tur xavfsizligini ta'minlashning eng samarali usulidir.
- Aniq Nomlash Qoidalari: Xabar turlaringiz va yuklama interfeyslaringiz uchun izchil va tavsiflovchi nomlardan foydalaning (masalan, `UserListResponse`, `ChatMessageReceived`).
- Xatolarni Boshqarish: Ham kliyent, ham serverda mustahkam xatolarni boshqarishni amalga oshiring. Maxsus xato xabari turlarini aniqlang va kliyentlar tegishli ravishda munosabat bildirishini ta'minlang.
- Yuklamalarni Yengil Saqlang: Xabarlaringizda faqat kerakli ma'lumotlarni yuboring. Bu samaradorlikni oshiradi va potentsial xatolar uchun sirtni kamaytiradi.
- Freymvorkni Ko'rib Chiqing: Socket.IO kabi kutubxonalar WebSocket'lar ustidan yuqori darajadagi abstraksiyalarni taklif qiladi va kuchli TypeScript yordamiga ega, bu esa amalga oshirishni soddalashtirishi va avtomatik qayta ulanish va zaxira mexanizmlari kabi xususiyatlarni taqdim etishi mumkin. Biroq, oddiyroq holatlar uchun TypeScript bilan mahalliy `WebSocket` API ko'pincha etarli.
- Testlash: WebSocket muloqotingiz uchun birlik va integratsiya testlarini yozing. TypeScript oldindan aytib bo'ladigan test ma'lumotlarini sozlashda va ishlovchilarning xabarlarni to'g'ri qayta ishlashini tekshirishda yordam beradi.
Xulosa
WebSocket'lar zamonaviy, interaktiv va real-time ilovalarni yaratish uchun ajralmas hisoblanadi. WebSocket dasturlash jarayoniga TypeScript'ni integratsiya qilish orqali siz kuchli ustunlikka ega bo'lasiz. TypeScript tomonidan taqdim etilgan statik turlash ma'lumotlarni qayta ishlash usulingizni o'zgartiradi, kompilyatsiya vaqtida xatolarni aniqlaydi, kod sifatini oshiradi, dasturchi unumdorligini oshiradi va natijada yanada ishonchli va qo'llab-quvvatlanadigan real-time tizimlariga olib keladi. Ilovaning barqarorligi va oldindan aytib bo'ladigan xatti-harakati muhim bo'lgan global auditoriya uchun turi xavfsiz WebSocket dasturlashiga sarmoya kiritish nafaqat eng yaxshi amaliyot, balki ajoyib foydalanuvchi tajribasini taqdim etish uchun zaruratdir.
TypeScript'ni qabul qiling, xabar shartnomalaringizni aniq belgilang va javob beruvchanligi kabi mustahkam bo'lgan real-time ilovalarni yarating.